Errors and Exceptions

Running into errors and exceptions is inevitable, and debugging is a huge part of modern-day product development. As of now, we've worked with the roots of Python syntax, and have encountered many types of errors that are built-in. Of course, as you proceed, you will pass more complex errors. Let's see how it's done.

Before explaining what an Exception is, let's look at a quick syntax error.


In [1]:
# Producing an Error
print("hey there)


  File "<ipython-input-1-659f9ef84117>", line 2
    print("hey there)
                     ^
SyntaxError: EOL while scanning string literal

What if we wanted to prevent this error from stopping our program? For that case, we use what's called try-except statements. Here's the general syntax.

try:
    // possible error inducing code
except Error:
    // handle the error
except AnotherError:
    // you can handle more than 1 error
else:
    // if no exception runs
finally:
    // run no matter what

This is the full syntax of try-except. We don't even need else or finally statements. However, we will use them after these basic examples.

Example 1 Let's try catching a divide by 0 error.


In [14]:
# Catching a SyntaxError
try:
    print(8/0)
except ZeroDivisionError:
    print("There is no dividing by 0")


There is no dividing by 0

Example 2 What if try sending in a string as a float?


In [15]:
try:
    user_input = float("Is this a float?")
except ValueError:
    print("Put a number in there, man!")


Put a number in there, man!

Let's quickly go over else and finally. Recall, the else block will run if no exception runs, and the finally will always run.

Example 3 Catching a TypeErrror (trying to mutate a string type - recall these sequences are immutable)


In [24]:
# Mutation of a String?
try:
    my_str = "hello"
    my_str[1] = "tr"
except TypeError:
    print("We've got a TypeError")
else:
    print("This shouldn't run!")
finally:
    print("Finally, I always print!")


We've got a TypeError
Finally, I always print!

Let's look at some other features.

You can check for more than one exception! Now, it's important to understand that except block will only handle the first exception it finds. Order them in a tuple!

One thing we have not discussed is that as soon as


In [31]:
# Broadening the except statement
try:
    str[0] == "h"
except (TypeError, SyntaxError):
    print("String mutate")


String mutate

In [36]:
# Trying to exce
try:
    print(4/0)
    str[1] = "e"
except (ZeroDivisionError, TypeError):
    print("what!")


what!

The last except (or the only one, if there's only one) doesn't need an Exception associated with it. It's know as the "default except".


In [39]:
try:
    print(4/0)
except:
    print("wow")


wow

In [41]:
# Catching the error in its respective statement
try:
    print(4/0)
except ZeroDivisionError:
    print("ZeroDiv Error")
except:
    print("There's an error, I just don't know what!")
else:
    print("There wasn't an error!")


ZeroDiv Error

In [42]:
# Catching the error in the deault, if the specific except fails
try:
    str[4] = "wow"
except ZeroDivisionError:
    print("ZeroDiv Error")
except:
    print("There's an error, I just don't know what!")
else:
    print("There wasn't an error!")


There's an error, I just don't know what!

Raising Errors

Sometimes, you may need to physically raise your own errors. This is possible with the raise keyword. It works as expected.


In [44]:
try:
    raise TypeError
except TypeError:
    print("Error raised")


Error raised

In [51]:
class MyException(Exception):
    def __init__(self, result):
        self.result = result
    def __str__(self):
        return "MyException occured, result that caused it: {}".format(self.result)

Below, we'll catch it, and treat it with "as" to shorten the name and not cause the statement to confuse MyException.


In [55]:
try:
    raise MyException("error string")
except MyException as myexcep:
    print(myexcep)


MyException occured, result that caused it: error string